package starter

import (
	"gitee.com/kristas/booting-go/framework"
	"gitee.com/kristas/booting-go/framework/core/bean"
	"gitee.com/kristas/booting-go/framework/core/log"
	"gitee.com/kristas/booting-go/framework/core/statement/types"
	"gitee.com/kristas/booting-go/framework/web"
	"gitee.com/kristas/booting-go/framework/web/web_filter"
	"reflect"
	"strings"
	"sync"
)

func init() {
	framework.Component(new(serverRunner))
}

type serverRunner struct {
	types.ApplicationRunner `order:"999"`
	Container               web.Server `wire:""`
	Log                     log.Logger `wire:""`
	Port                    int        `value:"server.port"`
}

func (s *serverRunner) Run() error {
	var once sync.Once
	web_filter.InitFilters(func(order int, filter web.Filter) {
		once.Do(func() {
			s.Log.Debug("using filters:")
		})
		s.Log.Debug("└%s %s [%s] [%3d]", strings.Repeat("-", order), reflect.TypeOf(filter).Elem().String(), strings.Join(filter.UrlPatterns(), ", "), filter.Order())
	})
	s.Log.Info("container detected: %s", bean.GetBeanName(s.Container))
	go func() {
		s.Log.Info("server listen at %d", s.Port)
		err := s.Container.ListenAndServe(s.Port)
		if err != nil {
			s.Log.Fatal(err.Error())
		}
	}()
	return nil
}

type ServerProcessor struct {
	Log  log.Logger `wire:""`
	Port int        `value:"server.port"`
}

func (s *ServerProcessor) Order() int {
	return 257
}

func (s *ServerProcessor) Async() bool {
	return true
}

func (s *ServerProcessor) Filter(bean bean.Bean) bool {
	_, ok := bean.(web.Server)
	return ok
}

func (s *ServerProcessor) Do(b bean.Bean) error {
	container := b.(web.Server)
	var once sync.Once
	web_filter.InitFilters(func(order int, filter web.Filter) {
		once.Do(func() {
			s.Log.Debug("using filters:")
		})
		s.Log.Debug("└%s %s [%s] [%3d]", strings.Repeat("-", order), reflect.TypeOf(filter).Elem().String(), strings.Join(filter.UrlPatterns(), ", "), filter.Order())
	})
	s.Log.Info("container detected: %s", bean.GetBeanName(container))
	go func() {
		err := container.ListenAndServe(s.Port)
		if err != nil {
			s.Log.Fatal(err.Error())
		}
	}()
	return nil
}
